// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import Logger from '../Logger';
const CAE_STREAM_DELIMITER_MAGICWORD = 0x5a5a;
const WEBSOCKET_OPEN_STATE = 1;
const HEART_BEAT_INTERVAL = 500;
const PACKAGE_HEADER_LENGTH = 8;
const MSG_TYPE_MAP = {
  cmdControl: 7,
  heartBeat: 8
};
const MSG_CMD_MAP = {
  start: '0',
  stop: '1',
  heartBeat: '2',
  reconnect: '4',
  pause: '5',
  recover: '6'
};

class SocketWorker {
  constructor() {
    this.ws = null;
    this.heartBeatTimer = null;
  }

  getCheckSum(msgType) {
    return (
      (msgType +
        ((CAE_STREAM_DELIMITER_MAGICWORD >> 8) & 0xff) +
        (CAE_STREAM_DELIMITER_MAGICWORD & 0xff)) &
      0xff
    );
  }

  makeActionMsg(msgType, msgCmd, paramStr) {
    let msgBody = 'command=' + MSG_CMD_MAP[msgCmd];
    paramStr && (msgBody += '&' + paramStr);

    const cmdBuf = new Uint8Array(PACKAGE_HEADER_LENGTH + msgBody.length);
    cmdBuf[0] = 90;
    cmdBuf[1] = 90;
    cmdBuf[2] = this.getCheckSum(MSG_TYPE_MAP[msgType]);
    cmdBuf[3] = MSG_TYPE_MAP[msgType];
    cmdBuf[4] = 0;
    cmdBuf[5] = 0;
    cmdBuf[6] = 0;
    cmdBuf[7] = msgBody.length;

    for (let i = 0, len = msgBody.length; i < len; i++) {
      cmdBuf[PACKAGE_HEADER_LENGTH + i] = msgBody.charCodeAt(i);
    }

    return cmdBuf.buffer;
  }

  startHeartbeat() {
    const buf = this.makeActionMsg('heartBeat', 'heartBeat');
    buf && this.ws.send(buf);
    this.heartBeatTimer && clearInterval(this.heartBeatTimer);
    this.heartBeatTimer = setInterval(() => {
      if (WEBSOCKET_OPEN_STATE === this.ws.readyState) {
        const buf = this.makeActionMsg('heartBeat', 'heartBeat');
        this.ws.send(buf);

        // 心跳发送数据数量超过maxCount则停止发送，避免异常断网等导致未及时收到响应导致数组过大。
        this.onHeartbeatSended && this.onHeartbeatSended(Date.now());
      } else {
        this.stopHeartbeat();
      }
    }, HEART_BEAT_INTERVAL);
  }

  stopHeartbeat() {
    clearInterval(this.heartBeatTimer);
    Logger.debug('Stop sending heartbeat.');
  }

  init(options) {
    this.ws = new WebSocket(options.protocol + '://' + options.connectURI);
    this.ws.binaryType = 'arraybuffer';

    this.ws.onopen = () => {
      this.onOpen && this.onOpen(this.ws.readyState);
      if (options.needHeatBeat) {
        this.startHeartbeat();
      }
    };

    this.ws.onmessage = (evt) => {
      this.onMessage && this.onMessage(evt.data);
    };

    this.ws.onerror = (evt) => {
      Logger.debug('ws error: ', evt);
      this.onError && this.onError();
    };

    this.ws.onclose = () => {
      this.onClose && this.onClose(this.ws.readyState);
    };
  }

  send(buf) {
    this.ws.send(buf);
  }

  processReq(req) {
    switch (req.cmd) {
      case 'initReq':
        this.init(req.options);
        break;
      case 'sendReq':
        this.send(req.data);
        break;
      case 'startHeartbeatReq':
        this.startHeartbeat();
        break;
      case 'stopHeartbeatReq':
        this.stopHeartbeat();
        break;
      case 'closeReq':
        this.destroy();
        break;
      case 'socketStateReq':
        this.onState(this.ws.readyState);
        break;
      default:
        Logger.debug('Unknown command socket worker received.');
    }
  }

  destroy() {
    this.ws && this.ws.close();
  }
}

if (typeof self !== 'undefined') {
  self.socket = new SocketWorker();
  self.socket.onOpen = (state) => {
    self.postMessage({ cmd: 'openRsp', state: state });
  };

  self.socket.onError = () => {
    self.postMessage({ cmd: 'errorRsp' });
  };

  self.socket.onMessage = (data) => {
    self.postMessage({ cmd: 'recvRsp', buf: data, time: Date.now() });
  };

  self.socket.onHeartbeatSended = (time) => {
    self.postMessage({ cmd: 'heartbeatRsp', time: time });
  };

  self.socket.onClose = (state) => {
    self.postMessage({ cmd: 'closeRsp', state: state });
  };

  self.socket.onState = (state) => {
    self.postMessage({ cmd: 'socketStateRsp', state });
  };

  self.addEventListener(
    'message',
    (evt) => {
      const req = evt.data;
      self.socket.processReq(req);
    },
    false
  );
}

export default SocketWorker;
